import aop.config.AopConfig;
import aop.service.MathCalculator;
import org.junit.jupiter.api.Test;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.DecoratingProxy;
import org.springframework.core.SpringVersion;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;
import org.springframework.objenesis.instantiator.ObjectInstantiator;

import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * AOP 原理
 *
 * <pre>
 * 1、由开启 AOP 功能的注解入手
 * 发现 @EnableAspectJAutoProxy 注解给容器中导入了 AspectJAutoProxyRegistrar
 * @see EnableAspectJAutoProxy
 * @see AspectJAutoProxyRegistrar
 *
 * 2、AspectJAutoProxyRegistrar 主要功能就是给容器中导入 AnnotationAwareAspectJAutoProxyCreator
 * @see AnnotationAwareAspectJAutoProxyCreator 在 IOC 中 Bean 定义信息的名称为 internalAutoProxyCreator
 * 导入过程中判断了 @EnableAspectJAutoProxy 注解上的一些属性
 *
 * 3、很明显重点就在 AnnotationAwareAspectJAutoProxyCreator 类上
 * 3.1、先看类继承关系
 * AnnotationAwareAspectJAutoProxyCreator
 * * => AspectJAwareAdvisorAutoProxyCreator
 * * * => AbstractAdvisorAutoProxyCreator
 * * * * => AbstractAutoProxyCreator
 * * * * * => ProxyProcessorSupport
 * * * * * -> SmartInstantiationAwareBeanPostProcessor 关键接口，可以在 Bean 初始化完成前后做事情
 * * * * * -> BeanFactoryAware 关键接口，自动装配 BeanFactory
 *
 * 3.2、找到接口的关键方法
 * AbstractAutoProxyCreator.setBeanFactory()
 * * => AbstractAdvisorAutoProxyCreator.setBeanFactory() 其中会调用 initBeanFactory()
 * * => AbstractAdvisorAutoProxyCreator.initBeanFactory()
 * * * => AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
 * AbstractAutoProxyCreator.有后置处理器的逻辑
 *
 * 3.3、DEBUG 执行流程
 * * 1、创建 IOC 容器，设置主配置类 ==> new AnnotationConfigApplicationContext(AopConfig.class)
 * * 2、注册配置类，初始化容器 ==> refresh()
 * @see AbstractApplicationContext#refresh()
 * * 3、注册 Bean 的后置处理器 ==> registerBeanPostProcessors(beanFactory)
 * @see AbstractApplicationContext#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
 * @see PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, org.springframework.context.support.AbstractApplicationContext)
 * * * 1、获取 IOC 中已定义的后置处理器 ==> beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false)
 * * * 2、获取向 IOC 中添加的其它后置处理器，例如这里的 AnnotationAwareAspectJAutoProxyCreator.class
 * * * 3、区分实现了 PriorityOrdered、Ordered 接口的，和没有实现的（执行优先级：PriorityOrdered > Ordered > 没实现的）
 * * * 4、主要看创建 AnnotationAwareAspectJAutoProxyCreator 实例创建
 * * * * 1、创建 Bean 实例 => createBeanInstance(beanName, mbd, args)
 * @see AbstractAutowireCapableBeanFactory#createBeanInstance(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
 * * * * 2、Bean 属性赋值 => populateBean(beanName, mbd, instanceWrapper)
 * @see AbstractAutowireCapableBeanFactory#populateBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, org.springframework.beans.BeanWrapper)
 * * * * 3、初始化 Bean
 * @see AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
 * * * * * 1、处理 Aware 接口的方法回调 => invokeAwareMethods(beanName, bean)
 * @see AbstractAutowireCapableBeanFactory#invokeAwareMethods(java.lang.String, java.lang.Object)
 * * * * * * 1、执行 AbstractAdvisorAutoProxyCreator => setBeanFactory()
 * * * * * * 2、执行 AnnotationAwareAspectJAutoProxyCreator => initBeanFactory()
 * * * * * 2、应用后置处理器的 BeforeInitialization 方法 => applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
 * @see AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization(java.lang.Object, java.lang.String)
 * * * * * 3、执行自定义的初始化方法 => invokeInitMethods(beanName, wrappedBean, mbd)
 * @see AbstractAutowireCapableBeanFactory#invokeInitMethods(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
 * * * * * 4、应用后置处理器的 AfterInitialization 方法 => applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
 * @see AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization(java.lang.Object, java.lang.String)
 * * * * 4、AnnotationAwareAspectJAutoProxyCreator 创建成功 => AnnotationAwareAspectJAutoProxyCreator() -> BeanFactoryAspectJAdvisorsBuilder
 * * * 5、向 IOC 容器中添加实例 => registerBeanPostProcessors(beanFactory, orderedPostProcessors)
 * @see AbstractApplicationContext#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
 * * * 6、完成初始化，创建剩下的单实例 Bean => finishBeanFactoryInitialization(beanFactory)
 * * * * 1、获取 IOC 容器中所有的 Bean 定义信息 => beanDefinitionNames
 * * * * 2、开始创建 => getBean(beanName)
 * * * * * 1、创建Bean是会先在IOC中获取，不存在才创建 => Object sharedInstance = getSingleton(beanName)
 * * * * * 2、获取Bean => etSingleton(beanName,回调函数)
 * * * * * 3、创建Bean => createBean(beanName, mbd, args)
 * * * * * 4、尝试获取代理对象 AOP,有则返回 => resolveBeforeInstantiation(beanName, mbdToUse)
 * * * * * 5、创建Bean => doCreateBean(beanName, mbdToUse, args)
 * * * * * 6、初始化Bean => initializeBean(beanName, exposedObject, mbd)
 * </pre>
 */
public class T06_Aop {

    /**
     * https://blog.csdn.net/f641385712/article/details/88952482
     */
    @Test
    public void aopTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);

        System.out.println("SpringVersion: " + SpringVersion.getVersion());
        System.out.println("================================================");
        MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
        // MathCalculator mathCalculator = (MathCalculator) applicationContext.getBean("mathCalculatorImpl");
        mathCalculator.div(1, 1);
        System.out.println("================================================");


        // expected single matching bean but found 2: mathCalculatorImpl,proxyFactoryBean
        // 如果通过类型获取，会找到两个 Bean：一个我们自己的实现类、一个 ProxyFactoryBean 所生产的代理类，
        // 而此处我们显然是希望要生成的代理类的，因此我们只能通过名称来(或者加上 @Primary)
        // MathCalculator bean = applicationContext.getBean(MathCalculator.class);
        MathCalculator bean = (MathCalculator) applicationContext.getBean("proxyFactoryBean");
        bean.div(1, 1);
        System.out.println("================================================");

        System.out.println(bean);
        System.out.println(bean.getClass()); // class com.sun.proxy.$Proxy28 用的 JDK 动态代理
        // 顺便说一句：这样也是没错得。因为 Spring AOP 代理出来的每个代理对象，都默认实现了这个接口（它是个标记接口）
        // 它这个也就类似于所有的 JDK 代理出来的，都是 Proxy 的子类是一样的思想
        SpringProxy springProxy = (SpringProxy) bean;

        System.out.println("================================================");
        mathCalculator.div(1, 0);

        applicationContext.close();
    }

    @Test
    public void aopJDKProxyTest() { // 接口方式
        ProxyFactory proxyFactory = new ProxyFactory(new Demo());
        proxyFactory.addAdvice((MethodBeforeAdvice) (method, args1, target) -> {
            System.out.println("你被拦截了：方法名为：" + method.getName() + " 参数为--" + Arrays.asList(args1));
        });

        DemoInterface demo = (DemoInterface) proxyFactory.getProxy();
        // 你被拦截了：方法名为：hello 参数为--[]
        // this demo show
        demo.hello();

        System.out.println(proxyFactory.getTargetClass()); // class aop.AopTest$Demo
        System.out.println(proxyFactory.getTargetSource()); // SingletonTargetSource for target object [aop.AopTest$Demo@fdefd3f]
        System.out.println(Arrays.asList(proxyFactory.getProxiedInterfaces())); // [interface aop.AopTest$DemoInterface]
        // [org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [aop.AopTest$$Lambda$34/0x00000008000a4c40@d83da2e]]
        System.out.println(Arrays.asList(proxyFactory.getAdvisors()));

        // 获取类型，看看是 JDK 代理还是 cglib 的
        System.out.println(demo instanceof Proxy); // true  所有的 JDK 代理都是继承自 Proxy 的
        System.out.println(demo instanceof DecoratingProxy); // true
        System.out.println(demo instanceof SpringProxy); // true
        System.out.println(demo.getClass()); // class aop.$Proxy7
        System.out.println(Proxy.isProxyClass(demo.getClass())); // true
        System.out.println(AopUtils.isCglibProxy(demo)); // false

        // 测试 Advised 接口、DecoratingProxy 的内容
        System.out.println(demo instanceof Advised); // true
        Advised advised = (Advised) demo;
        System.out.println(Arrays.asList(advised.getProxiedInterfaces())); // [interface aop.AopTest$DemoInterface]
        // [org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [aop.AopTest$$Lambda$34/0x00000008000a4c40@d83da2e]]
        System.out.println(Arrays.asList(advised.getAdvisors()));
        System.out.println(advised.isExposeProxy()); // false
        System.out.println(advised.isFrozen()); // false

        // System.out.println(advised.removeAdvice(null));
        DecoratingProxy decoratingProxy = (DecoratingProxy) demo;
        System.out.println(decoratingProxy.getDecoratedClass()); // class aop.AopTest$Demo

        System.out.println("-----------------------------------------------------");

        // Object 的方法 ==== 所有的 Object 方法都不会被 AOP 代理，这点需要注意
        System.out.println(demo.equals(new Object())); // false
        System.out.println(demo.hashCode());
        System.out.println(demo.getClass()); // class aop.$Proxy7

        // 其余方法都没被拦截，只有 toString() 被拦截了
        System.out.println(demo.toString());
        // 你被拦截了：方法名为：hello 参数为--[]
        // aop.AopTest$Demo@fdefd3f
    }

    @Test
    public void aopCGLIBProxyTest() { // 子类方式
        ProxyFactory proxyFactory = new ProxyFactory(new Demo());
        proxyFactory.addAdvice((MethodBeforeAdvice) (method, args1, target) -> {
            System.out.println("你被拦截了：方法名为：" + method.getName() + " 参数为--" + Arrays.asList(args1));
        });

        Demo demo = (Demo) proxyFactory.getProxy();
        // 你被拦截了：方法名为：hello 参数为--[]
        // this demo show
        demo.hello();

        System.out.println(proxyFactory.getTargetClass()); // class aop.AopTest$Demo
        System.out.println(proxyFactory.getTargetSource()); // SingletonTargetSource for target object [aop.AopTest$Demo@76505305]
        System.out.println(Arrays.asList(proxyFactory.getProxiedInterfaces())); // []
        // [org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [aop.AopTest$$Lambda$34/0x00000008000a4c40@14cd1699]]
        System.out.println(Arrays.asList(proxyFactory.getAdvisors()));

        // 获取类型，看看是 JDK 代理还是 cglib 的
        System.out.println(demo instanceof SpringProxy); // true
        System.out.println(demo instanceof DecoratingProxy); // false，CGLIB 代理出来的对象没有实现接口 DecoratingProxy
        System.out.println(demo.getClass()); // class aop.AopTest$Demo$$EnhancerBySpringCGLIB$$8179ec5b
        System.out.println(Proxy.isProxyClass(demo.getClass())); // false
        System.out.println(AopUtils.isCglibProxy(demo)); // true

        // 测试 Advised 接口、DecoratingProxy 的内容
        System.out.println(demo instanceof Advised); // true
        Advised advised = (Advised) demo;
        System.out.println(Arrays.asList(advised.getProxiedInterfaces())); // []
        //[org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [aop.AopTest$$Lambda$34/0x00000008000a4c40@14cd1699]]
        System.out.println(Arrays.asList(advised.getAdvisors()));
        System.out.println(advised.isExposeProxy()); // false
        System.out.println(advised.isFrozen()); // false

        System.out.println("-----------------------------------------------------");

        // Object 的方法 ==== 所有的 Object 方法都不会被 AOP 代理
        System.out.println(demo.equals(new Object())); // false
        System.out.println(demo.hashCode());
        System.out.println(demo.getClass()); // class aop.AopTest$Demo$$EnhancerBySpringCGLIB$$8179ec5b

        // 其余方法都没被拦截，只有 toString() 被拦截了
        // 你被拦截了：方法名为：hello 参数为--[]
        // aop.AopTest$Demo@76505305
        System.out.println(demo.toString());
    }

    interface DemoInterface {
        void hello();
    }

    // static class Demo implements DemoInterface {
    static class Demo {
        // @Override
        public void hello() {
            System.out.println("this demo show");
        }
    }

    // ==============================================================================================================

    @Test
    public void objenesisStdTest() throws IllegalAccessException, InstantiationException {
        Objenesis objenesis = new ObjenesisStd();
        // 它竟然创建成功了
        MyDemo myDemo = objenesis.newInstance(MyDemo.class);
        System.out.println(myDemo); // aop.AopTest$MyDemo@2f0a87b3
        System.out.println(myDemo.code); // null，特别注意：这里是 null，而不是 10

        // 相当于生成了一个实例创建的工厂，接下来就可以很方便得创建实例了
        // 如果你要创建多个实例，建议这么来创建
        ObjectInstantiator<MyDemo> instantiator = objenesis.getInstantiatorOf(MyDemo.class);
        MyDemo myDemo1 = instantiator.newInstance();
        MyDemo myDemo2 = instantiator.newInstance();
        System.out.println(myDemo1);
        System.out.println(myDemo1.code); // null
        System.out.println(myDemo2);

        // 若直接这样创建，就报错 java.lang.InstantiationException: aop.AopTest$MyDemo
        System.out.println(MyDemo.class.newInstance());
    }

    static class MyDemo {
        public String code = "10";

        public MyDemo(String code) {
            this.code = code;
        }
    }
}
